/***************************************************************************
 *                                                                         *
 *   This file is part of the Fotowall project,                            *
 *       http://code.google.com/p/fotowall                                 *
 *                                                                         *
 *   Copyright (C) 2007-2009 by Enrico Ros <enrico.ros@gmail.com>          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 * ----------------------------------------------------------------------- *
 *																		   *
 *	Modifications for FotoKinect, Spring 2011 by Gianna Badiali,		   *
 *                                                   Gabriel Fernandez,	   *
 *                                                   Kelsey Tripp, and     *
 *                                                   Sam Swarr             *
 ***************************************************************************/

#include "Desk.h"
#include "frames/FrameFactory.h"
#include "items/ColorPickerItem.h"
#include "items/HighlightItem.h"
#include "items/PictureContent.h"
#include "items/PictureConfig.h"
#include "CroppingDialog.h"
#include "RenderOpts.h"
#include <QAbstractTextDocumentLayout>
#include <QFile>
#include <QGraphicsSceneDragDropEvent>
#include <QGraphicsView>
#include <QImageReader>
#include <QLabel>
#include <QList>
#include <QMessageBox>
#include <QMimeData>
#include <devices\InputPoint.h>
#include "MainWindow.h"
#include <QThread>
#include <QTextDocument>
#include <QTimer>
#include <QUrl>
#include <iostream>
#include <QtCore/qmath.h>
#include <QDesktopWidget>

#define COLORPICKER_W 200
#define COLORPICKER_H 150

Desk::Desk(QObject * parent)
    : QGraphicsScene(parent)

    , m_backContent(0)
    , m_backGradientEnabled(true)
    , m_backContentRatio(Qt::KeepAspectRatioByExpanding)

{

    // create colorpicker
    m_colorPicker = new ColorPickerItem(COLORPICKER_W, COLORPICKER_H, 0);
    m_colorPicker->setColor(QColor(192, 192, 192));
    m_colorPicker->setAnchor(ColorPickerItem::AnchorBottomLeft);
    m_colorPicker->setZValue(10000);
    connect(m_colorPicker, SIGNAL(colorChanged(const QColor&)), this, SLOT(slotGradColorChanged()));
    addItem(m_colorPicker);

    QTransform t;
    t.translate(width(),0);
    t.scale(4.5,4.5);
    m_colorPicker->setTransform(t);

    // selection self-hook
    connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));

    // precreate background tile
    m_backTile = QPixmap(100, 100);
    m_backTile.fill(Qt::lightGray);
    QPainter tilePainter(&m_backTile);
    tilePainter.fillRect(0, 0, 50, 50, Qt::darkGray);
    tilePainter.fillRect(50, 50, 50, 50, Qt::darkGray);
    tilePainter.end();

    // to keep track of currently selected values
    m_selected = NULL;
    m_cropSelection = NULL;
    m_lastColor = m_colorPicker->color();

    // crazy background stuff
#if 0
#define RP QPointF(-400 + qrand() % 2000, -300 + qrand() % 1500)
    for ( int i = 0; i < 100; i++ ) {
        QGraphicsPathItem * p = new QGraphicsPathItem();
        addItem(p);
        p->show();
        p->setPen(QPen(QColor::fromHsv(qrand() % 20, 128 + qrand() % 127, qrand() % 255)));
        QPainterPath path(RP);
        path.cubicTo(RP, RP, RP);
        p->setPath(path);
    }
#endif

}

Desk::~Desk()
{
    qDeleteAll(m_highlightItems);
    delete m_colorPicker;
    qDeleteAll(m_content);
    m_content.clear();
    m_backContent = 0;
}

// Finds a point near the center of the desk (used to add content)
static QPoint nearCenter(const QRectF & rect)
{
    return rect.center().toPoint() + QPoint(2 - (qrand() % 5), 2 - (qrand() % 5));
}

// Adds pictures to the desk
void Desk::addPictures(const QStringList & fileNames)
{
    QPoint pos = nearCenter(sceneRect());
    foreach (const QString & localFile, fileNames) {
        if (!QFile::exists(localFile)) {
            continue;
        }

        // create picture and load the file
        PictureContent * p = createPicture(pos);
        if (!p->loadPhoto(localFile, true, true)) {
            m_content.removeAll(p);
            delete p;
        } else
            pos += QPoint(30, 30);
    }
}


// Calls the slot that deletes selected content
bool Desk::deletePicture() {

    // check for selected content
    if(m_selected) {

        // remove from desk
        m_content.removeAll(m_selected);
        m_selected->dispose();

        // deselect the cursors
        this->slotDeselect(InputPoint(0,0,0,1));
        this->slotDeselect(InputPoint(0,0,0,2));

        m_selected = NULL;
        return true;
    }

    return false;
}


// resizes the Desk
void Desk::resize(const QSize & size)
{
    // relayout contents
    m_size = size;
    m_rect = QRectF(0, 0, m_size.width(), m_size.height());
    m_colorPicker->setPos(size.width() - COLORPICKER_W, 0);
    foreach (HighlightItem * highlight, m_highlightItems) {
        highlight->reposition(m_rect);
    }

    // ensure visibility
    foreach (AbstractContent * content, m_content) {
        content->ensureVisible(m_rect);
    }
    foreach (AbstractConfig * config, m_configs) {
        config->keepInBoundaries(m_rect.toRect());
    }

    // change the rect
    setSceneRect(m_rect);
}


/// Misc: save, restore, ...
#define HIGHLIGHT(x, y, del) \
{ \
  HighlightItem * highlight = new HighlightItem(); \
                              if (!del) m_highlightItems.append(highlight); \
                              else highlight->deleteAfterAnimation(); \
                              addItem(highlight); \
                              highlight->setZValue(10000); \
                              highlight->setPosF(x, y); \
                              highlight->show(); \
 }



// creates XML doc in order to save collage
void Desk::toXml(QDomElement & de) const
{
    QDomDocument doc = de.ownerDocument();

    // save background Colors
    QColor color;
    QString r, g, b;
    QDomElement domElement, topColor, bottomColor,
    redElement = doc.createElement("red"),
    greenElement = doc.createElement("green"),
    blueElement = doc.createElement("blue"),
    redElement2 = doc.createElement("red"),
    greenElement2 = doc.createElement("green"),
    blueElement2 = doc.createElement("blue"),
    rElement2 = doc.createElement("red"),
    gElement2 = doc.createElement("green"),
    bElement2 = doc.createElement("blue");
    domElement = doc.createElement("background-color");

    topColor = doc.createElement("top");
    color = m_colorPicker->color();
    r.setNum(color.red()); g.setNum(color.green()); b.setNum(color.blue());
    redElement.appendChild(doc.createTextNode(r));
    greenElement.appendChild(doc.createTextNode(g));
    blueElement.appendChild(doc.createTextNode(b));
    topColor.appendChild(redElement); topColor.appendChild(greenElement); topColor.appendChild(blueElement);
    domElement.appendChild(topColor);

    de.appendChild(domElement);

    // save back content aspect
    QDomElement backRatioElement = doc.createElement("back-properties");
    backRatioElement.setAttribute("ratio", (int)m_backContentRatio);
    de.appendChild(backRatioElement);
}


// generates collage from XML doc
void Desk::fromXml(QDomElement & de)
{

    QDomElement domElement;
    int r, g, b;

    domElement = de.firstChildElement("background-color").firstChildElement("top");
    r = domElement.firstChildElement("red").text().toInt();
    g = domElement.firstChildElement("green").text().toInt();
    b = domElement.firstChildElement("blue").text().toInt();
    m_colorPicker->setColor(QColor(r, g, b));

    domElement = de.firstChildElement("back-properties");
    if (domElement.isElement())
        m_backContentRatio = (Qt::AspectRatioMode)domElement.attribute("ratio").toInt();

    update();
}


// renders content on Desk
void Desk::renderVisible(QPainter * painter, const QRectF & target, const QRectF & source, Qt::AspectRatioMode aspectRatioMode)
{
    clearSelection();
    foreach(QGraphicsItem *item, m_markerItems) {
        item->hide();
    }
    foreach(AbstractConfig *conf, m_configs) {
        conf->hide();
    }

    RenderOpts::HQRendering = true;
    QGraphicsScene::render(painter, target, source, aspectRatioMode);
    RenderOpts::HQRendering = false;

    foreach(AbstractConfig *conf, m_configs) {
        conf->show();
    }
    foreach(QGraphicsItem *item, m_markerItems) {
        item->show();
    }
}


// renders the collage to save
QImage Desk::renderedImage(const QSize & iSize, Qt::AspectRatioMode aspectRatioMode)
{
    QImage result(iSize, QImage::Format_ARGB32);
    result.fill(0);

    QPainter painter(&result);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);

    QSize targetSize = sceneRect().size().toSize();
    targetSize.scale(iSize, aspectRatioMode);

    int offsetX = (iSize.width() - targetSize.width()) / 2;
    int offsetY = (iSize.height() - targetSize.height()) / 2;

    QRect targetRect = QRect(offsetX, offsetY, targetSize.width(), targetSize.height());
    renderVisible(&painter, targetRect, sceneRect(), Qt::IgnoreAspectRatio);
    painter.end();

    return result;
}


// moves the ColorSelector to the appropriate spot on the Desk
void Desk::showColorSelector(){
    QTransform t;
    t.translate(-860,85);
    t.scale(4.5,4.5);
    m_colorPicker->setTransform(t);
}


// updates ColorSelector's hue or color with the color at the given point in scene coordinates
// scene is usually MainWindow
void Desk::updateColorSelector(QPoint pos) {
    // translate scene coordinates to ColorSelector coordinates
    QPointF colorPos = m_colorPicker->mapFromScene(QPointF((float) pos.x(), (float) pos.y()));
    // pick the color (hue/value) at the point
    m_colorPicker->pickColor(QPoint((int) colorPos.x(), (int) colorPos.y()));
}


// updates ColorSelector's hue with the color at the given point in scene coordinates
// scene is usually MainWindow
void Desk::updateColorSelectorHue(QPoint pos) {
    // translate scene coordinates to ColorSelector coordinates
    QPointF colorPos = m_colorPicker->mapFromScene(QPointF((float) pos.x(), (float) pos.y()));
    // pick the hue at the point
    m_colorPicker->pickHue(QPoint((int) colorPos.x(), (int) colorPos.y()));
}


// updates COlorSelector's value with the color at the given point in scene coordinates
// scene is usually MainWindow
void Desk::updateColorSelectorValue(QPoint pos) {
    // translate scene coordinates to ColorSelector coordinates
    QPointF colorPos = m_colorPicker->mapFromScene(QPointF((float) pos.x(), (float) pos.y()));
    // pick the value at the point
    m_colorPicker->pickValue(QPoint((int) colorPos.x(), (int) colorPos.y()));
}


// tests to see if a given point in scene coordinates is within the bounds of the ColorSelector (either hue or value selector)
// scene is usually MainWindow
bool Desk::withinColorSelector(QPoint pos) {
    // translate scene coordinates to ColorSelector coordinates
    QPointF colorPos = m_colorPicker->mapFromScene(QPointF((float) pos.x(), (float) pos.y()));

    // test to ensure the point is within ColorSelector's bounding rectangle
    if (colorPos.x() > 0 && colorPos.x() < m_colorPicker->boundingRect().width() && colorPos.y() > 0 && colorPos.y() < m_colorPicker->boundingRect().height()) {
        return true;
    }
    else {
        return false;
    }
}


// resets the color of the desk to be the last selected color
void Desk::resetColorSelector() {
    m_colorPicker->resetColor(m_lastColor);
    update();
}


// receives signal from MainWindow when a color selection has been made
void Desk::slotUpdateLastColor() {
    // keep track of the last color chosen
    m_lastColor = m_colorPicker->color();
}


// moves the ColorSelector out of view when not in use
void Desk::hideColorSelector(){
    QTransform t;
    t.translate(0,height());
    m_colorPicker->setTransform(t);
}


// Draws the scene's background
void Desk::drawBackground(QPainter * painter, const QRectF & rect)
{
    // draw content if set
    if (m_backContent) {
        // regenerate cache if needed
        QSize sceneSize = sceneRect().size().toSize();
        if (m_backCache.isNull() || m_backCache.size() != sceneSize)
            m_backCache = m_backContent->renderContent(sceneSize, m_backContentRatio);

        // paint cached background
        QRect targetRect = rect.toRect();
        if (m_backContent->contentOpaque())
            painter->setCompositionMode(QPainter::CompositionMode_Source);
        painter->drawPixmap(targetRect, m_backCache, targetRect);
        if (m_backContent->contentOpaque())
            painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
        return;
    }

    // draw background gradient, if enabled
    if (m_backGradientEnabled) {
        QLinearGradient lg(m_rect.topLeft(), m_rect.bottomLeft());
        lg.setColorAt(0.0, m_colorPicker->color());
        painter->setCompositionMode(QPainter::CompositionMode_Source);
        painter->fillRect(rect, lg);
        painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
        return;
    }

    // draw checkboard to simulate a transparent background
    if (!RenderOpts::ARGBWindow && !RenderOpts::HQRendering) {
        QRect tileRect = rect.toAlignedRect();
        painter->drawTiledPixmap(tileRect, m_backTile, QPointF(tileRect.left() % 100, tileRect.top() % 100));
    }
}


// draws a vertical shadow on the screen
static void drawVerticalShadow(QPainter * painter, int width, int height)
{
    QLinearGradient lg( 0, 0, 0, height );
    lg.setColorAt( 0.0, QColor( 0, 0, 0, 64 ) );
    lg.setColorAt( 0.4, QColor( 0, 0, 0, 16 ) );
    lg.setColorAt( 0.7, QColor( 0, 0, 0, 5 ) );
    lg.setColorAt( 1.0, QColor( 0, 0, 0, 0 ) );
    painter->fillRect( 0, 0, width, height, lg );
}


// draws the scene's foreground
void Desk::drawForeground(QPainter * painter, const QRectF & rect)
{
    // draw header/footer
    const int top = (int)rect.top();
    const int left = (int)rect.left();
    const int width = (int)(rect.width() + 1.0);

    // draw top shadow (only on screen)
    if (!RenderOpts::HQRendering) {
        // the first time create the Shadow Tile
        static QPixmap shadowTile;
        if (shadowTile.isNull()) {
            shadowTile = QPixmap(64, 8);
            shadowTile.fill(Qt::transparent);
            QPainter shadowPainter(&shadowTile);
            drawVerticalShadow(&shadowPainter, 64, 8);
        }

        // blend the shadow tile
        if (top < 8)
            painter->drawTiledPixmap(left, 0, width, 8, shadowTile);
    }
}


// initializes and configures content on the desk
void Desk::initContent(AbstractContent * content, const QPoint & pos)
{
    connect(content, SIGNAL(configureMe(const QPoint &)), this, SLOT(slotConfigureContent(const QPoint &)));
    connect(content, SIGNAL(backgroundMe()), this, SLOT(slotBackgroundContent()));
    connect(content, SIGNAL(changeStack(int)), this, SLOT(slotStackContent(int)));
    connect(content, SIGNAL(deleteItem()), this, SLOT(slotDeleteContent()));

    if (!pos.isNull()) {
        content->setPos(pos);
    }
    content->setZValue(m_content.isEmpty() ? 1 : (m_content.last()->zValue() + 1));
    content->show();

    m_content.append(content);
}


// sets up the background content when saving/loading
void Desk::setBackContent(AbstractContent * content)
{
    // skip if unchanged
    if (content == m_backContent)
        return;

    // re-show previous background
    if (m_backContent) {
        disconnect(m_backContent, SIGNAL(contentChanged()), this, SLOT(slotBackContentChanged()));
        m_backContent->show();
    }

    // hide content item
    m_backContent = content;
    if (m_backContent) {
        connect(m_backContent, SIGNAL(contentChanged()), this, SLOT(slotBackContentChanged()));
        m_backContent->hide();
    }

    // hide gradient controls
    m_colorPicker->hide();

    // update GUI
    m_backCache = QPixmap();
    update();
    emit backModeChanged();
}


// adds a picture to the desk at the given point
PictureContent * Desk::createPicture(const QPoint & pos)
{
    PictureContent * p = new PictureContent(this);
    initContent(p, pos);
    connect(p, SIGNAL(flipHorizontally()), this, SLOT(slotFlipHorizontally()));
    connect(p, SIGNAL(flipVertically()), this, SLOT(slotFlipVertically()));
    return p;
}


// updates the currently selected item
void Desk::slotSelectionChanged()
{
    // show the config widget if 1 AbstractContent is selected
    QList<QGraphicsItem *> selection = selectedItems();
    if (selection.size() == 1) {
        AbstractContent * content = dynamic_cast<AbstractContent *>(selection.first());
        if (content) {
            QWidget * pWidget = content->createPropertyWidget();
            if (pWidget)
                pWidget->setWindowTitle(tr("%1").arg(content->contentName().toUpper()));
            emit showPropertiesWidget(pWidget);
            return;
        }
    }

    // show a 'selection' properties widget
    if (selection.size() > 1) {
        QLabel * label = new QLabel(tr("%1 objects selected").arg(selection.size()));
        label->setWindowTitle(tr("SELECTION"));
        emit showPropertiesWidget(label);
        return;
    }

    // or don't show anything
    emit showPropertiesWidget(0);
}


// configures content on the desk
void Desk::slotConfigureContent(const QPoint & scenePoint)
{
    // get the content and ensure it hasn't already a property window
    AbstractContent * content = dynamic_cast<AbstractContent *>(sender());
    foreach (AbstractConfig * config, m_configs) {
        if (config->content() == content)
            return;
        // force only 1 property instance
        slotDeleteConfig(config);
    }
    AbstractConfig * p = 0;

    // picture config (dialog and connections)
    if (PictureContent * picture = dynamic_cast<PictureContent *>(content)) {
        p = new PictureConfig(picture);
        connect(p, SIGNAL(applyEffect(const PictureEffect &, bool)), this, SLOT(slotApplyEffect(const PictureEffect &, bool)));
    }


    // generic config
    if (!p)
        p = new AbstractConfig(content);

    // common links
    m_configs.append(p);
    addItem(p);
    connect(p, SIGNAL(applyLook(quint32,bool,bool)), this, SLOT(slotApplyLook(quint32,bool,bool)));
    p->show();
    p->setPos(scenePoint - QPoint(10, 10));
    p->keepInBoundaries(sceneRect().toRect());
    p->setFocus();
}


// sets the background
void Desk::slotBackgroundContent()
{
    setBackContent(dynamic_cast<AbstractContent *>(sender()));
}


// updates the z-index of layered content on the desk
void Desk::slotStackContent(int op)
{
    AbstractContent * content = dynamic_cast<AbstractContent *>(sender());
    if (!content || m_content.size() < 2)
        return;
    int size = m_content.size();
    int index = m_content.indexOf(content);

    // find out insertion indexes over the stacked items
    QList<QGraphicsItem *> stackedItems = items(content->sceneBoundingRect(), Qt::IntersectsItemShape);
    int prevIndex = 0;
    int nextIndex = size - 1;
    foreach (QGraphicsItem * item, stackedItems) {
        // operate only on different Content
        AbstractContent * c = dynamic_cast<AbstractContent *>(item);
        if (!c || c == content)
            continue;

        // refine previous/next indexes (close to 'index')
        int cIdx = m_content.indexOf(c);
        if (cIdx < nextIndex && cIdx > index)
            nextIndex = cIdx;
        else if (cIdx > prevIndex && cIdx < index)
            prevIndex = cIdx;
    }

    // move items
    switch (op) {
    case 1: // front
        m_content.append(m_content.takeAt(index));
        break;
    case 2: // raise
        if (index >= size - 1)
            return;
        m_content.insert(nextIndex, m_content.takeAt(index));
        break;
    case 3: // lower
        if (index <= 0)
            return;
        m_content.insert(prevIndex, m_content.takeAt(index));
        break;
    case 4: // back
        m_content.prepend(m_content.takeAt(index));
        break;
    }

    // reassign z-levels
    int z = 1;
    foreach (AbstractContent * content, m_content)
        content->setZValue(z++);
}


// adds content to the desk
static QList<AbstractContent *> content(const QList<QGraphicsItem *> & items) {
    QList<AbstractContent *> contentList;
    foreach (QGraphicsItem * item, items) {
        AbstractContent * c = dynamic_cast<AbstractContent *>(item);
        if (c) {
            contentList.append(c);
        }
    }
    return contentList;
}


// deletes content from the desk
void Desk::slotDeleteContent()
{
    QList<AbstractContent *> selectedContent = content(selectedItems());
    AbstractContent * senderContent = dynamic_cast<AbstractContent *>(sender());
    if (senderContent && !selectedContent.contains(senderContent)) {
        selectedContent.clear();
        selectedContent.append(senderContent);
    }
    if (selectedContent.size() > 1)
        if (QMessageBox::question(0, tr("Delete content"), tr("All the %1 selected content will be deleted, do you want to continue ?").arg(selectedContent.size()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
            return;

    foreach (AbstractContent * content, selectedContent) {

        // unset background if deleting its content
        if (m_backContent == content)
            setBackContent(0);

        // remove related property if deleting its content
        foreach (AbstractConfig * config, m_configs) {
            if (config->content() == content) {
                slotDeleteConfig(config);
                break;
            }
        }

        // unlink content from lists, myself(the Scene) and memory
        m_content.removeAll(content);
        content->dispose();
    }
}


// deletes configs from the desk
void Desk::slotDeleteConfig(AbstractConfig * config)
{
    m_configs.removeAll(config);
    config->dispose();
}


// sets up the cropping widget and picture to be cropped
void Desk::beginCropMode(CroppingWidget &croppingWidget) {

    // only set up if there is content selected
    if (m_selected != NULL) {

        // gets the pixmap of the selected image
        PictureContent * picture = dynamic_cast<PictureContent *>(m_selected);
        CPixmap photo = picture->getPhoto();

        // set the cropping widget's pixmap to display selected image
        croppingWidget.setPixmap(&photo);

        // save the width and height for cropping ratio later
        m_savedWidth = m_selected->contentsRect().width();
        m_savedHeight = m_selected->contentsRect().height();

        // keep track of the selected image for actual cropping later,
        // and deselect the content
        m_cropSelection = m_selected;
        this->slotDeselect(InputPoint(0,0,0,1));
        this->slotDeselect(InputPoint(0,0,0,2));
        m_selected = NULL;

    }
}


// performs the actual image cropping
bool Desk::cropPicture(QRect cropSelection) {

    if(!cropSelection.isNull()) {

        // apply the crop effect to the stored image to be cropped
        PictureContent * picture = dynamic_cast<PictureContent *>(m_cropSelection);

        if (picture->addEffect(PictureEffect(PictureEffect::Crop, 0, cropSelection))) {

            // if the crop was successful, scale to the appropriate ratio
            double ratio = 1;
            if (picture->contentsRect().width() > picture->contentsRect().height()){
                ratio = m_savedWidth/picture->contentsRect().width();
            }
            else{
                ratio = m_savedHeight/picture->contentsRect().height();
            }

            // resize the cropped photo
            double newWidth = picture->contentsRect().width() * ratio;
            double newHeight = picture->contentsRect().height() * ratio;
            picture->resizeContents(QRect(-newWidth / 2, -newHeight / 2, newWidth, newHeight));

            return true;
        }
        else {
            return false;
        }
    }
}


// flips the selected photo horizontally
void Desk::slotFlipHorizontally()
{
    QList<AbstractContent *> selectedContent = content(selectedItems());
    foreach (AbstractContent * content, selectedContent) {
        PictureContent * picture = dynamic_cast<PictureContent *>(content);
        if (!picture)
            continue;
        picture->addEffect(PictureEffect::FlipH);
    }
}


// flips the selected photo vertically
void Desk::slotFlipVertically()
{
    QList<AbstractContent *> selectedContent = content(selectedItems());
    foreach (AbstractContent * content, selectedContent) {
        PictureContent * picture = dynamic_cast<PictureContent *>(content);
        if (!picture)
            continue;
        picture->addEffect(PictureEffect::FlipV);
    }
}


// changes the foreground color
void Desk::slotForeColorChanged()
{
    update(0, 0, m_size.width(), 50);
    update(0, m_size.height() - 50, m_size.width(), 50);
}


// changes the gradient color
void Desk::slotGradColorChanged()
{
    update();
}


// changes the background image
void Desk::slotBackContentChanged()
{
    m_backCache = QPixmap();
    update();
}


// checks for cursor hover over content
void Desk::slotCheckHover(QList<QPoint> points)
{
    if (m_selected == NULL){

        foreach(AbstractContent *c, m_content) {
            c->setControlsVisible(false);
        }

        foreach(QPoint p, points){

            double maxZ = 0;
            AbstractContent *ontop = NULL;

            foreach(AbstractContent *c, m_content) {
                QPointF coord = c->mapFromParent(p.x(), p.y());
                QRectF cursorRect(coord.x(), coord.y(), 30, 30);
                bool contain = c->boundingRect().intersects(cursorRect);
                if (contain){
                    if(c->zValue() > maxZ){
                        maxZ = c->zValue();
                        ontop = c;
                    }
                }

            }

            if(ontop != NULL){
                ontop->setControlsVisible(true);
            }
        }
    }
}


// selects a photo on the desk
/* returns true if a PictureContent was selected, false if not. */
bool Desk::slotSelect(InputPoint p)
{
    MainWindow *mw = (MainWindow *)(this->parent());
    bool picSelected = false;
    double maxZ = 0;
    AbstractContent *ontop = NULL;

    //Loop through all content, and check which one is first
    //under the cursor.
    foreach(AbstractContent *c, m_content) {
        QPointF coord = c->mapFromParent(p.x()*width(), height()-p.y()*height());
        QRect cursor(coord.x()-15, coord.y()-15, 15, 15);

        bool contain = c->boundingRect().intersects(cursor);
        if (contain) {
            picSelected = true;
            if(c->zValue() > maxZ){
                maxZ = c->zValue();
                ontop = c;
            }
        }
        if (c != m_selected){
            c->setControlsVisible(false);
        }
    }

    if (ontop != NULL){
        //Only mark as 'selected' if nothing else is
        if (m_selected == NULL){
            ontop->setControlsVisible(true);
            ontop->slotStackFront();
            m_selected = ontop;
            if(p.id() == 1)
                mw->setCursorGreen(1);
            else
                mw->setCursorGreen(2);
            picSelected = true;
        }

        //Only check what part it was selected for InputPoint
        //state if gesture is on m_selected.
        if (ontop == m_selected){
            QPointF coord = ontop->mapFromParent(p.x()*width()-15, height()-p.y()*height()-15);
            InputState state = ontop->whereSelect(coord.x(), coord.y());
            m_inputsSelect.insert(p.id());
            emit signalInputStateChange(p.id(), state);
        }
    }
    return picSelected;
}


// deselects the content selected by the given input point
void Desk::slotDeselect(InputPoint p)
{
    MainWindow *mw = (MainWindow *)(this->parent());

    if (m_inputsSelect.contains(p.id())){
        m_inputsSelect.remove(p.id());
        if(p.id() == 1)
            mw->slotRevertCursor1();
        else
            mw->slotRevertCursor2();
        emit signalInputStateChange(p.id(), FREE_MOTION);
    }

    if (m_inputsSelect.empty() && m_selected != NULL){
        m_selected->setControlsVisible(false);
        m_selected = NULL;
    }
}


// saves the state of the currently selected content
void Desk::slotSaveState()
{
    if (m_selected != NULL) {
        m_savedPos = m_selected->pos();
        m_savedRect = m_selected->contentsRect();
        m_savedRotationZ = m_selected->rotation(Qt::ZAxis);
        m_savedRotationY = 0;
        m_savedRotationX = 0;
        m_savedTransform = m_selected->transform();
    }


		m_isSaved = true;
		m_isPrepared = false;
}


// moves the selected content by a given x and y distance
void Desk::slotMove(double dx, double dy)
{
    if (m_selected != NULL) {
        QPointF coord(m_selected->pos().x() + dx*width(), m_selected->pos().y() - dy*height());
        m_selected->setPos(coord);
    }
}


// prepares the selected content to be transformed
void Desk::slotPrepareTransform(qreal x1, qreal y1, qreal z1, qreal x2, qreal y2, qreal z2)
{

	if (m_selected != NULL) {
		slotSaveState();
		
		m_line1 = QLineF(x1*width(), height()-y1*height(), x2*width(), height()-y2*height());
		QPointF p((m_line1.x1()+ m_line1.x2())/2, (m_line1.y1()+m_line1.y2())/2);
		
		QPointF rectmid = QPointF(m_savedPos.x()+m_savedRect.width()/2, m_savedPos.y()+m_savedRect.height()/2);
		m_diff = p - m_savedPos;

		m_isPrepared = true;

	}
}

void Desk::slotUnsave(){
	m_isSaved = false;
	m_isPrepared = false;
}

#include <QtCore/qmath.h>

// performs the actual translation on the selected content
void Desk::slotTransform(qreal x1, qreal y1, qreal z1, qreal x2, qreal y2, qreal z2)
{		
	if (m_selected != NULL  && m_isPrepared) {

        //Change line between two input points to mainwindow space.
        QLineF line2(x1*width(), height()-y1*height(), x2*width(), height()-y2*height());
        QPointF mpXY((line2.x1()+line2.x2())/2, (line2.y1()+line2.y2())/2);  //midpoint in XY plane

        //XY transformations
        double scale = line2.length()/m_line1.length();
        double angleZ = m_line1.angleTo(line2);

        slotScale(scale);
        slotRotate(-angleZ, Qt::ZAxis);

        QLineF diffLine(QPointF(0,0), m_diff);
        diffLine.setLength(diffLine.length()*scale);
        diffLine.setAngle(diffLine.angle()+angleZ);

        m_selected->setPos(mpXY.x()-diffLine.x2(), mpXY.y()-diffLine.y2());
    }
}


// scales the selected content by a given percentage
void Desk::slotScale(double perc)
{
	if (m_selected != NULL) {
		int H, W;
		if (m_isSaved){
			double ratio = m_savedRect.width()/m_savedRect.height();
			H = m_savedRect.height()*perc;
			W = H*ratio;
		}else{
			H = m_selected->contentsRect().height()*perc;
			W = m_selected->contentsRect().width()*perc;
		}

		m_selected->resizeContents(QRect(-W / 2, -H / 2, W, H));
	}
}


// rotates the selected content by the given angle along the given axis
void Desk::slotRotate(double angle, Qt::Axis axis)
{
	if (m_selected != NULL) {
		double rz, ry, rx;
		if(m_isSaved){
			rz = m_savedRotationZ;
			ry = m_savedRotationY;
			rx = m_savedRotationX;
		}else{
			rz = m_selected->rotation(Qt::ZAxis);
			ry = m_selected->rotation(Qt::YAxis);
			rx = m_selected->rotation(Qt::XAxis);
		}


		if (axis == Qt::ZAxis){
			m_selected->setRotation(rz + angle, axis);
		}else if (axis == Qt::YAxis){
			m_selected->setRotation(ry + angle, axis);
		}else if (axis == Qt::XAxis){
			m_selected->setRotation(rx + angle, axis);
		}

	}
}


// flips the selected content vertically or horizontally
/* Horizontal Flip = 0, Vertical Flip != 0 */
void Desk::slotFlip(int i)
{	
    if (m_selected != NULL){
        PictureContent * picture = dynamic_cast<PictureContent *>(m_selected);
        if (i == 0){
            if (picture)
                picture->addEffect(PictureEffect::FlipH);
        }
        else {
            if (picture)
                picture->addEffect(PictureEffect::FlipV);
        }
    }
}
